use rand::{self, Rng};
use std::ops::{Add, Index, IndexMut, Mul, Sub};

use super::{MatrixView, MatrixViewMut};

#[derive(Debug, Clone, PartialEq)]
pub struct Vector {
    data: Vec<f64>
}

impl Vector {
    pub fn new(size: usize) -> Self {
        Self::zeros(size)
    }

    pub fn zeros(size: usize) -> Self {
        Self { data: vec![0.; size] }
    }

    pub fn ones(size: usize) -> Self {
        Self { data: vec![1.; size] }
    }

    pub fn randoms(size: usize, (min, max): (f64, f64)) -> Self {
        let mut rng = rand::rng();
        Self {
            data: (0..size).map(|_| rng.random_range(min..=max)).collect()
        }
    } 

    pub fn from_vec(v: Vec<f64>) -> Self {
        Self { data: v }
    }
}

impl Vector {
    pub fn fill(&mut self, v: f64) {
        self.data.iter_mut().for_each(|e| *e = v);
    }

    pub fn size(&self) -> usize {
        self.data.len()
    }

    pub fn row_view(&self) -> MatrixView {
        MatrixView::new(
            &self.data,
            1,
            self.size(),
            self.size(),
        )
    }

    pub fn row_view_mut(&mut self) -> MatrixViewMut {
        let size = self.size();
        MatrixViewMut::new(
            &mut self.data,
            1,
            size,
            size,
        )   
    }

    pub fn col_view(&self) -> MatrixView {
        MatrixView::new(
            &self.data,
            self.size(),
            1,
            1
        )
    }

    pub fn col_view_mut(&mut self) -> MatrixViewMut {
        let size = self.size();
        MatrixViewMut::new(
            &mut self.data,
            size,
            1,
            1,
        )   
    }

    pub fn copy_from(&mut self, other: &Self) {
        self.row_view_mut().copy_from(other.row_view());
    } 
}

#[macro_export]
macro_rules! vector {
    ($($x:expr),*) => {
        {
            let mut vec = Vec::new();
            $(
                vec.push($x as f64);
            )*
            Vector::from_vec(vec)
        }
    };
    
    ($x:expr; $n:expr) => {
        {
            let mut vec = Vec::with_capacity($n);
            for _ in 0..$n {
                vec.push($x as f64);
            }
            Vector::from_vec(vec)
        }
    };
}

impl Index<usize> for Vector {
    type Output = f64;
    fn index(&self, index: usize) -> &Self::Output {
        &self.data[index]
    }
}

impl IndexMut<usize> for Vector {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.data[index]
    }
}

impl Sub<&Vector> for &Vector {
    type Output = Vector;
    fn sub(self, rhs: &Vector) -> Self::Output {
        (self.row_view() - rhs.row_view()).to_vector()
    }
}

impl Sub<Vector> for Vector {
    type Output = Vector;
    fn sub(self, rhs: Vector) -> Self::Output {
        &self - &rhs
    }
}

impl Sub<&Vector> for Vector {
    type Output = Vector;
    fn sub(self, rhs: &Vector) -> Self::Output {
        &self - rhs
    }
}

impl Sub<Vector> for &Vector {
    type Output = Vector;
    fn sub(self, rhs: Vector) -> Self::Output {
        self - &rhs
    }
}

impl Add<&Vector> for &Vector {
    type Output = Vector;
    fn add(self, rhs: &Vector) -> Self::Output {
        (self.row_view() + rhs.row_view()).to_vector()
    }
}

impl Add<Vector> for Vector {
    type Output = Vector;
    fn add(self, rhs: Vector) -> Self::Output {
        &self + &rhs
    }
}

impl Add<&Vector> for Vector {
    type Output = Vector;
    fn add(self, rhs: &Vector) -> Self::Output {
        &self + rhs
    }
}

impl Add<Vector> for &Vector {
    type Output = Vector;
    fn add(self, rhs: Vector) -> Self::Output {
        self + &rhs
    }
}

impl Mul<&Vector> for &Vector {
    type Output = Vector;
    fn mul(self, rhs: &Vector) -> Self::Output {
        (self.row_view() * rhs.row_view()).to_vector()
    }
}

impl Mul<Vector> for Vector {
    type Output = Vector;
    fn mul(self, rhs: Vector) -> Self::Output {
        &self * &rhs
    }
}

impl Mul<&Vector> for Vector {
    type Output = Vector;
    fn mul(self, rhs: &Vector) -> Self::Output {
        &self * rhs
    }
}

impl Mul<Vector> for &Vector {
    type Output = Vector;
    fn mul(self, rhs: Vector) -> Self::Output {
        self * &rhs
    }
}